#include <QImage>
#include "MeshRenderer.h"
#include "RendererHistoryEvent.h"
#include "HistoryManager.h"

/**	Handles the event when the OpenGL window size changes.
 */
void QGLSurfacesRenderer::onGLWindowResize( int w, int h )
{
	if( modelFrame )
	{
		modelFrame->t.setDimensions( (GLfloat)w, (GLfloat)h );
		modelFrame->t.updateProjectionMatrix( );
	}
	glViewport(0, 0, w, h);
	updateGL();
}

/**	Handles the event when a mouse button is double clicked.
 */
void QGLSurfacesRenderer::onMouseDoubleClick( QMouseEvent* _event )
{
	if( modelFrame && selectionMode == EDGE )
		emit( clearSharpEdges() );
}


/**	Handles the event when a mouse button is depressed.
 */
void QGLSurfacesRenderer::onMouseDown( QMouseEvent* _event )
{
	// clear the mouse moved flag
	mouseMoved = false;

	if( modelFrame )
		modelFrame->t.storeLastPoint( _event->pos() );
}


/**	Handles the event when the mouse moves while depressed.
 */
void QGLSurfacesRenderer::onMouseMove( QMouseEvent* _event )
{  
	QPoint p = _event->pos(); 

	// set flag denoting mouse movement
	mouseMoved = true;

	//check if model is loaded
	if( modelFrame )
	{
		// enable GL context
		makeCurrent();
	 
		// move in x,y direction
		if ( _event->buttons() == Qt::RightButton )
			modelFrame->t.moveXY( p );
		
		// rotate
		else if( _event->buttons() == Qt::LeftButton ) 
			modelFrame->t.activateRotate( p );

		// do the hover selection
		if (_event->buttons() == Qt::NoButton &&
			this->animating	== false &&
			this->zooming == false)
		{
			Vec3d windowPoint(_event->x(), _event->y(), 0);
			doHoverSelection(windowPoint);
		}
		else
		{
			selectionHover = NULL;
		}

		// trigger redraw
		updateGL();
	}

	// change the cursor to indicate dragging
	if (_event->buttons() != Qt::NoButton &&
		this->lastKnownCursor == Qt::ArrowCursor)
	{
		this->lastKnownCursor = this->cursor().shape();
		this->setCursor(QCursor(Qt::ClosedHandCursor));
	}
}


/**	Handles the event when a mouse button is released.
 */
void QGLSurfacesRenderer::onMouseUp( QMouseEvent* _event )
{  
	PolyMesh* importanceMesh;

	//check if there is a model loaded
	if( modelFrame && selectionMode != NONE )
	{	
		modelFrame->t.setLastPointOK( false );

		// perform selection near mouse
		if (!this->mouseMoved)
		{
			Vec3d windowPoint(_event->x(), _event->y(), 0);
			doSelection(windowPoint);
		}

		// aaaand redraw...
		updateGL();
	}

	if (this->mouseMoved)
	{
		this->setCursor(QCursor(this->lastKnownCursor));
		this->lastKnownCursor = Qt::ArrowCursor;
	}
}


/**	Handles the event when the mouse scroll-wheel is used.
 */
void QGLSurfacesRenderer::onWheelScroll(QWheelEvent* _event)
{
	//check if there is a model loaded
	if( modelFrame )
	{
		// zoom into an imaginary, centered sphere
		if (this->modelFrame != NULL)
		{
			if (this->modelFrame->getImportanceMesh() != NULL)
			{
				if (this->modelFrame->getImportanceMesh()->n_vertices() != 0)
				{
					zoomToMesh(*this->modelFrame->getImportanceMesh());
				}
			}
			else if (this->modelFrame->reconnected != NULL)
			{
				zoomToMesh(*this->modelFrame->reconnected);
			}
		}

		// this part is where the zooming happens
		if (_event->delta() > 0)
		{
			this->oldBounds.radius *= 1.1;
		}
		else
		{
			this->oldBounds.radius *= 0.9;
		}
		modelFrame->t.setScenePosition(Vec3f(this->oldBounds.center), 
			                           this->oldBounds.radius);

		// zooming wont occur more than once without this
		this->bounds = oldBounds; 

		updateGL();
		_event->accept();
	}
}


/**	Handles the event dispatched by the model frame when there
 *	is a new "area of importance" defined.
 */
void QGLSurfacesRenderer::onModelImportanceChanged()
{
	PolyMesh* importanceMesh;

	// can't have a null mesh
	if (this->modelFrame == NULL)
	{
		return;
	}

	// can't have an empty mesh
	if (this->modelFrame->getImportanceMesh()->n_vertices() == 0)
	{
		return;
	}
	
	importanceMesh = this->modelFrame->getImportanceMesh();
	if (importanceMesh != NULL)
	{
		zoomToMesh(*importanceMesh);
		radiusInc = oldBounds.radius - bounds.radius;
		radiusInc /= ZOOM_RAD_STEPS;
		interpBounds = oldBounds;
		zoomInterpVal = 0.0;
		startZoomAnimation();
	}

	this->zoomComplete = false;
	this->zooming = true;
}


/**	Handles the event when a new model is loaded into the
 *	ModelLocator.
 */
void QGLSurfacesRenderer::onModelLoaded()
{
	ModelLocator*	modelLocator;
	ModelFrame*		newFrame;
	PolyMesh*		coarseMesh;
	Vec3f			bbMin, bbMax;

	PolyMesh::ConstVertexIter vIt;
	PolyMesh::ConstVertexIter vEnd;

	modelLocator = ModelLocator::instance();
	newFrame = modelLocator->getFrame();

	if (newFrame == NULL)
	{
		return; // false alarm, so return
	}

	if (this->modelFrame != NULL)
	{
		//delete this->modelFrame;
	}

	this->modelFrame = newFrame;

	if (!newFrame->signalConnected)
	{
		connect(this->modelFrame, SIGNAL(importanceChanged()),
				this,             SLOT(onModelImportanceChanged()));
		newFrame->signalConnected = true;
	}

	// Get the model set up for rendering...

	initTransformations(newFrame);
	coarseMesh = newFrame->coarse;
	coarseMesh->request_face_normals( );
	coarseMesh->request_vertex_normals( );

	// update face and vertex normals     
	if ( ! newFrame->loadOptions.check( IO::Options::FaceNormal ) )
	  coarseMesh->update_face_normals();

	if ( ! newFrame->loadOptions.check( IO::Options::VertexNormal ) )
	  coarseMesh->update_vertex_normals();

	// bounding box     
	vIt        = coarseMesh->vertices_begin();
	vEnd       = coarseMesh->vertices_end();
	bbMin      = OpenMesh::vector_cast<Vec3f>( coarseMesh->point(vIt) );
	bbMax      = bbMin;
	for (size_t count = 0; vIt != vEnd; ++vIt, ++count)
	{
		bbMin.minimize( OpenMesh::vector_cast<Vec3f>( coarseMesh->point(vIt)) );
		bbMax.maximize( OpenMesh::vector_cast<Vec3f>( coarseMesh->point(vIt)) );
	}

	// set center and radius in frame
	newFrame->t.setOrigScenePosition( (bbMin + bbMax)*0.5, (bbMin - bbMax).norm()*0.5 );

	centerModel( true );
	this->selection = NULL;
	this->selectionHover = NULL;
	newFrame->setActive( ModelFrame::RECONNECTED );
	updateGL();
}


void QGLSurfacesRenderer::animateSliderChanged(int changedVal)
{
	interpVal = (double)(changedVal/100.0); 
	Vec3d tempPoint,temp1, temp2;

	if( modelFrame )
	{
		// set/reset the "animating" flag
		this->animating = interpVal > 0.0? true : false;

		// interpolate new vertex locations
		modelFrame->setActive( ModelFrame::ANIMATED );
		for(PolyMesh::VertexIter vit=this->modelFrame->animated.vertices_begin(); vit != this->modelFrame->animated.vertices_end(); ++vit)
		{
			temp1 = modelFrame->reconnected->point(vit.handle());
			temp2 = modelFrame->subdivided->point(vit.handle());
			tempPoint = (1.0-interpVal)*temp1 + (interpVal)*temp2;

			modelFrame->animated.set_point(vit.handle(),(PolyMesh::Point)tempPoint);
		}

		modelFrame->animated.request_face_normals();
		modelFrame->animated.request_vertex_normals();
		modelFrame->animated.update_normals();
		updateGL();
	}
}
